/* GMServer.cc -- Servidor
 *
 * Copyright (C) 2008, Diogo F. S. Ramos <diogo.ramos@usp.br>
 * GEAR - Grupo de Estudos Avançados em Robótica
 * <http://www.sel.eesc.usp.br/gear>
 *
 * This file is part of GEARMedula.
 * 
 * GEARMedula is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, version 3 of the License.
 * 
 * GEARMedula is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
//#include <errno.h>
#include <stdlib.h>
//#include <unistd.h>
//#include <sys/types.h>
//#include <sys/socket.h>
#include <netinet/in.h>
//#include <netdb.h>
#include <arpa/inet.h>
//#include <pthread.h>
#include <string.h>

#include <vector>

#include "GMServer.hh"
#include "Messages.hh"
#include "Fields.hh"


GMServer::GMServer(void)
{
    socketfd_ = 0;    
}


GMServer::~GMServer(void)
{
    // Pare a thread do servidor, sim?
    Stop();
}

void
GMServer::Init( unsigned int port, Monitor * monitor, Control * control )
{
    CreateSocket( port );

    if(listen( socketfd_, 1 ) < 0 )
	xerror_.Fatal("server:Não consegui me tornar um servidor.");

    /* Iniciando o conjunto de file descriptors */
    FD_ZERO( &active_fd_set_ );
    FD_SET( socketfd_, &active_fd_set_ );

    services_.setMonitor( monitor );
    services_.setControl( control );

    rpc_translator_.Init( received_data_buffer_,
                          sended_data_buffer_ );

}

void
GMServer::CreateSocket( unsigned int port )
{
    struct sockaddr_in name;
    
    /* Create the socket. */
    socketfd_ = socket (PF_INET, SOCK_STREAM, 0);
    if (socketfd_ < 0)
	xerror_.Fatal("server: Não consegui criar um socket.");
     
    /* Give the socket a name. */
    name.sin_family = AF_INET;
    name.sin_port = htons (port);
    name.sin_addr.s_addr = htonl (INADDR_ANY);
    if (bind (socketfd_, (struct sockaddr *) &name, sizeof (name)) < 0)
	xerror_.Fatal("server: Não consegui realizar bind "
                      "no socket recém criado.");
}


int
GMServer::SendMessage( unsigned int socket_in )
{
    int bytes_sent = 0;
    while( bytes_sent < kBufferSize )
    {
        int bytes_writen = 0;
        bytes_writen = send( socket_in,
                             sended_data_buffer_,
                             kBufferSize,
                             0 );
        if( bytes_sent < 0 )
        {
            return -1;
        }
        bytes_sent += bytes_writen;
    }
    return 0;
}


int
GMServer::ReadAndDecodeMessage(unsigned int socket_in )
{
    // Armazenar mensagem no buffer
    ssize_t bytes_read = read( socket_in,
                               (void*) &received_data_buffer_,
                               kBufferSize);
    
    // Mensagem enviada recebida com sucesso
    if( bytes_read > 0)
    {
        MessageType message_type;
        std::vector<struct FieldContainer> arguments_parsed;
        rpc_translator_.Receive( &message_type,
                                 arguments_parsed,
                                 RPCSIDE_SERVER);

        std::vector<struct FieldContainer> arguments_returned;
        // Caso seja necessário retorno de informação ao requerente.
        if( services_.ExecServiceOfMessage( message_type,
                                            arguments_parsed,
                                            arguments_returned ) )
        {
            rpc_translator_.Send( message_type,
                                  arguments_returned,
                                  RPCSIDE_SERVER);
            
            SendMessage( socket_in );
        }

        return 1;
            
    }
    else if( bytes_read == 0 )
    {
        return -1;
    }
    else
    {
        xerror_.Fatal("Não consegui ler a mensagem do cliente. Deprimi!");
        return 0;
    }

}



void
GMServer::Run( void * ptr )
{
    int i = 0;
    struct sockaddr_in clientname;

    xerror_.Info("server: Servidor pronto e esperando.");
    while (1)
    {
	/* Block until input arrives on one or more active sockets. */
	read_fd_set_ = active_fd_set_;
	if (select (FD_SETSIZE, &read_fd_set_, NULL, NULL, NULL) < 0)
	    xerror_.Fatal("server: Tive problemas na chamada select");
    

	/* Service all the sockets with input pending. */
	for (i = 0; i < FD_SETSIZE; ++i)
	    if (FD_ISSET (i, &read_fd_set_))
	    {
		if (i == socketfd_)
		{
		    /* Connection request on original socket. */
		    int new_con;
                    socklen_t length_ptr;
                    if( getsockname( i,
                                     (struct sockaddr *) &clientname,
                                     &length_ptr ) == -1 )
                    {
                        xerror_.Error("Não consegui extrair as informação"
                                      " de endereço e tamanho do socket");
                    }
                    
		    new_con = accept (socketfd_,
				      (struct sockaddr *) &clientname,
				      &length_ptr);
		    if (new_con < 0)
			xerror_.Fatal("server: Problemas em aceitar a conexão"
                                      " requisitada");
		    xerror_.Info ( "server: Conexão do host %s, porta %d.",
                                   inet_ntoa (clientname.sin_addr),
                                   ntohs (clientname.sin_port));
		    FD_SET (new_con, &active_fd_set_);
		}
		else
		{                    
		    // /* Data arriving on an already-connected socket. */
		    if (ReadAndDecodeMessage( i ) < 0)
		    {
		    	close (i);
		    	FD_CLR (i, &(active_fd_set_));
		    }
		}
	    }
    }
    
}
